/*
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package com.sun.faces.application.annotation;

import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;

import javax.faces.context.FacesContext;
import javax.faces.render.ClientBehaviorRenderer;
import javax.faces.render.FacesBehaviorRenderer;
import javax.faces.render.FacesRenderer;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.RenderKit;
import javax.faces.render.Renderer;
import javax.faces.FactoryFinder;
import javax.faces.FacesException;

/**
 * <p> <code>ConfigAnnotationHandler</code> {@link FacesRenderer} annotated classes.</p>
 */
public class RenderKitConfigHandler implements ConfigAnnotationHandler {

    private static final Collection<Class<? extends Annotation>> HANDLES;

    static {
        Collection<Class<? extends Annotation>> handles =
              new ArrayList<>(2);
        handles.add(FacesRenderer.class);
        handles.add(FacesBehaviorRenderer.class);
        HANDLES = Collections.unmodifiableCollection(handles);
    }

    Map<Class<?>, Annotation> annotatedRenderers;

    // ------------------------------------- Methods from ComponentConfigHandler


    /**
     * @see com.sun.faces.application.annotation.ConfigAnnotationHandler#getHandledAnnotations()
     */
    @Override
    public Collection<Class<? extends Annotation>> getHandledAnnotations() {

        return HANDLES;

    }


    /**
     * @see com.sun.faces.application.annotation.ConfigAnnotationHandler#collect(Class, java.lang.annotation.Annotation)
     */
    @Override
    public void collect(Class<?> target, Annotation annotation) {

        if (annotatedRenderers == null) {
            annotatedRenderers = new HashMap<>();
        }
        annotatedRenderers.put(target,  annotation);

    }


    /**
     * @see com.sun.faces.application.annotation.ConfigAnnotationHandler#push(javax.faces.context.FacesContext)
     */
    @Override
    public void push(FacesContext ctx) {


        if (annotatedRenderers != null) {
            RenderKitFactory rkf = (RenderKitFactory) FactoryFinder
            .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
			for (Map.Entry<Class<?>, Annotation> entry : annotatedRenderers
					.entrySet()) {
				Class<?> rClass = entry.getKey();
				if (entry.getValue() instanceof FacesRenderer) {
					FacesRenderer ra = (FacesRenderer) entry.getValue();
					try {
						RenderKit rk = rkf.getRenderKit(ctx, ra.renderKitId());
						if (rk == null) {
							throw new IllegalStateException(
									"Error processing annotated Renderer "
											+ ra.toString()
											+ " on class "
											+ rClass.getName()
											+ ".  Unable to find specified RenderKit.");
						}
						rk.addRenderer(ra.componentFamily(), ra.rendererType(),
								(Renderer) rClass.newInstance());
					} catch (IllegalStateException | InstantiationException | IllegalAccessException e) {
						throw new FacesException(e);
					}
				} else if (entry.getValue() instanceof FacesBehaviorRenderer) {
					FacesBehaviorRenderer bra = (FacesBehaviorRenderer) entry
							.getValue();
					try {
						RenderKit rk = rkf.getRenderKit(ctx, bra.renderKitId());
						if (rk == null) {
							throw new IllegalStateException(
									"Error processing annotated ClientBehaviorRenderer "
											+ bra.toString()
											+ " on class "
											+ rClass.getName()
											+ ".  Unable to find specified RenderKit.");
						}
						rk.addClientBehaviorRenderer(bra.rendererType(),
								(ClientBehaviorRenderer) rClass.newInstance());
					} catch (IllegalStateException | InstantiationException | IllegalAccessException e) {
						throw new FacesException(e);
					}
				}
			}
		}

    }

}
